import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import threading
import time
import shutil
import os
import hashlib
import json
import sys
from datetime import datetime
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

def get_resource_path(relative_path):
    """获取资源文件的绝对路径，兼容打包后的exe文件"""
    if getattr(sys, 'frozen', False):
        # 如果是打包的exe文件
        if hasattr(sys, '_MEIPASS'):
            # PyInstaller临时目录中的资源文件
            temp_path = os.path.join(sys._MEIPASS, relative_path)
            if os.path.exists(temp_path):
                return temp_path
        # 如果临时目录中没有，尝试exe文件所在目录
        base_path = os.path.dirname(sys.executable)
    else:
        # 如果是脚本运行，使用脚本所在目录
        base_path = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(base_path, relative_path)

def find_app_icon():
    """查找应用程序图标文件"""
    # 图标文件名优先级列表（优先PNG，因为用户指定要使用logo.png）
    icon_names = [
        'logo.png',      # 用户指定的图标
        'logo.ico', 
        'icon.png',
        'icon.ico', 
        'app_icon.png',
        'app_icon.ico', 
        'application.png',
        'application.ico'
    ]
    
    # 在程序目录中查找图标
    for icon_name in icon_names:
        icon_path = get_resource_path(icon_name)
        if os.path.exists(icon_path):
            return icon_path
    
    return None

def set_window_icon(window):
    """为窗口设置图标"""
    try:
        icon_path = find_app_icon()
        if icon_path:
            # 根据文件扩展名选择合适的设置方法
            if icon_path.lower().endswith('.ico'):
                # ICO格式直接使用iconbitmap
                window.iconbitmap(icon_path)
                print(f"✓ 已设置窗口图标(ICO): {icon_path}")
            elif icon_path.lower().endswith(('.png', '.jpg', '.jpeg')):
                # PNG/JPG格式使用PhotoImage
                try:
                    photo = tk.PhotoImage(file=icon_path)
                    window.iconphoto(True, photo)
                    print(f"✓ 已设置窗口图标(PNG): {icon_path}")
                except Exception as e:
                    print(f"⚠ PNG图标设置失败，尝试其他格式: {e}")
                    # 如果PNG失败，尝试查找ICO格式
                    ico_path = icon_path.replace('.png', '.ico')
                    if os.path.exists(ico_path):
                        window.iconbitmap(ico_path)
                        print(f"✓ 已设置窗口图标(ICO备用): {ico_path}")
            else:
                print(f"⚠ 不支持的图标格式: {icon_path}")
        else:
            print("⚠ 未找到图标文件，使用默认图标")
            print("提示: 可以将图标文件命名为 logo.png 或 logo.ico 放在程序目录中")
    except Exception as e:
        print(f"⚠ 设置窗口图标失败: {e}")
        # 忽略图标设置错误，不影响程序正常运行

class FileSyncHandler(FileSystemEventHandler):
    def __init__(self, source_folder, target_folder, status_callback, exclude_checker=None, is_bidirectional=False, conflict_resolver=None, sync_direction="A_to_B", global_sync_lock=None):
        self.source_folder = Path(source_folder)
        self.target_folder = Path(target_folder)
        self.status_callback = status_callback
        self.exclude_checker = exclude_checker
        self.is_bidirectional = is_bidirectional
        self.conflict_resolver = conflict_resolver
        self.sync_direction = sync_direction  # "A_to_B" 或 "B_to_A"
        self.global_sync_lock = global_sync_lock or threading.Lock()  # 全局同步锁，防止循环同步
        
    def on_any_event(self, event):
        if event.is_directory:
            return
            
        # 使用全局锁防止循环同步
        with self.global_sync_lock:
            try:
                # 获取事件文件路径
                event_path = Path(event.src_path)
                
                # 确定监控的文件夹（事件发生的文件夹）
                monitored_folder = None
                if event_path.is_relative_to(self.source_folder):
                    monitored_folder = self.source_folder
                elif event_path.is_relative_to(self.target_folder):
                    monitored_folder = self.target_folder
                else:
                    # 事件不在监控的文件夹内，忽略
                    return
                
                # 计算相对路径
                relative_path = event_path.relative_to(monitored_folder)
                
                # 检查是否应该排除此文件
                if self.exclude_checker and self.exclude_checker(relative_path):
                    return
                
                # 计算目标路径（同步到另一个文件夹）
                if monitored_folder == self.source_folder:
                    target_path = self.target_folder / relative_path
                else:
                    target_path = self.source_folder / relative_path
                
                if event.event_type == 'created' or event.event_type == 'modified':
                    self._handle_file_change(event_path, target_path, relative_path)
                    
                elif event.event_type == 'deleted':
                    self._handle_file_deletion(target_path, relative_path)
                        
                elif event.event_type == 'moved':
                    self._handle_file_move(event, relative_path, monitored_folder)
                        
            except Exception as e:
                self.status_callback(f"错误: {str(e)}")
                
    def _handle_file_change(self, event_path, target_path, relative_path):
        """处理文件创建或修改事件"""
        # 检查源文件是否存在
        if not event_path.exists():
            return
            
        # 检查目标文件是否存在且需要冲突解决
        if self.is_bidirectional and target_path.exists() and self.conflict_resolver:
            if not self._resolve_conflict(event_path, target_path, relative_path):
                return  # 冲突解决决定不同步
                
        # 确保目标目录存在
        target_path.parent.mkdir(parents=True, exist_ok=True)
        # 复制文件
        shutil.copy2(event_path, target_path)
        direction = "→" if self.sync_direction == "A_to_B" else "←"
        self.status_callback(f"已同步 {direction}: {relative_path}")
        
    def _handle_file_deletion(self, target_path, relative_path):
        """处理文件删除事件"""
        if target_path.exists():
            target_path.unlink()
            direction = "→" if self.sync_direction == "A_to_B" else "←"
            self.status_callback(f"已删除 {direction}: {relative_path}")
            
    def _handle_file_move(self, event, relative_path, monitored_folder):
        """处理文件移动事件"""
        old_relative_path = Path(event.src_path).relative_to(monitored_folder)
        new_relative_path = Path(event.dest_path).relative_to(monitored_folder)
        
        # 计算目标文件夹中的对应路径
        if monitored_folder == self.source_folder:
            old_target_path = self.target_folder / old_relative_path
            new_target_path = self.target_folder / new_relative_path
        else:
            old_target_path = self.source_folder / old_relative_path
            new_target_path = self.source_folder / new_relative_path
        
        if old_target_path.exists():
            new_target_path.parent.mkdir(parents=True, exist_ok=True)
            shutil.move(str(old_target_path), str(new_target_path))
            direction = "→" if self.sync_direction == "A_to_B" else "←"
            self.status_callback(f"已移动 {direction}: {old_relative_path} -> {new_relative_path}")
            
    def _resolve_conflict(self, event_path, target_path, relative_path):
        """解决文件冲突，返回True表示应该同步事件文件到目标"""
        try:
            event_stat = event_path.stat()
            target_stat = target_path.stat()
            
            strategy = self.conflict_resolver()
            
            if strategy == "较新文件优先":
                # 比较修改时间
                return event_stat.st_mtime > target_stat.st_mtime
            elif strategy == "较大文件优先":
                # 比较文件大小
                return event_stat.st_size > target_stat.st_size
            elif strategy == "A文件夹优先":
                # A文件夹优先：如果是A→B方向，总是同步；如果是B→A方向，不同步
                return self.sync_direction == "A_to_B"
            else:
                return True  # 默认同步
                
        except Exception as e:
            self.status_callback(f"冲突解决失败 {relative_path}: {str(e)}")
            return True  # 出错时默认同步

class MainApp:
    def __init__(self, root):
        self.root = root
        self.root.title("文件同步与Git管理工具")
        self.root.geometry("900x800")
        self.root.resizable(True, True)
        
        # 设置窗口图标
        set_window_icon(self.root)
        
        self.setup_ui()
        
    def setup_ui(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(0, weight=1)
        main_frame.rowconfigure(0, weight=1)
        
        # 创建Tab控件
        self.notebook = ttk.Notebook(main_frame)
        self.notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 创建文件同步Tab
        self.file_sync_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.file_sync_frame, text="文件同步")
        
        # 创建Git同步Tab
        self.git_sync_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.git_sync_frame, text="Git同步")
        
        # 初始化文件同步功能
        self.file_sync_app = FileSyncTab(self.file_sync_frame)
        
        # 初始化Git同步功能
        self.git_sync_app = GitSyncTab(self.git_sync_frame)
    
    def on_closing(self):
        """程序关闭时的处理"""
        # 检查文件同步是否正在进行
        if hasattr(self.file_sync_app, 'is_syncing') and self.file_sync_app.is_syncing:
            if messagebox.askokcancel("退出", "文件同步正在进行中，确定要退出吗？"):
                self.file_sync_app.stop_sync()
            else:
                return
        
        # 检查Git同步是否正在进行
        if hasattr(self.git_sync_app, 'is_git_syncing') and self.git_sync_app.is_git_syncing:
            if messagebox.askokcancel("退出", "Git同步正在进行中，确定要退出吗？"):
                self.git_sync_app.stop_git_sync()
            else:
                return
        
        # 保存配置
        if hasattr(self.file_sync_app, 'save_history'):
            self.file_sync_app.save_history()
        if hasattr(self.file_sync_app, 'save_exclude_rules'):
            self.file_sync_app.save_exclude_rules()
        if hasattr(self.git_sync_app, 'save_git_config'):
            self.git_sync_app.save_git_config()
        
        self.root.destroy()

class GitSyncTab:
    def __init__(self, parent_frame):
        self.parent_frame = parent_frame
        
        # Git同步相关变量
        self.repo_path = tk.StringVar()
        self.remote_url = tk.StringVar()
        self.branch_name = tk.StringVar(value="main")
        self.commit_message = tk.StringVar(value="自动同步")
        self.auto_commit = tk.BooleanVar(value=True)
        self.auto_push = tk.BooleanVar(value=False)
        self.sync_interval = tk.IntVar(value=300)  # 同步间隔（秒）
        self.is_git_syncing = False
        self.git_sync_timer = None
        self.git_observer = None
        self.git_config_file = get_resource_path("git_config.json")
        self.last_push_time = None  # 记录最后推送时间
        
        self.setup_ui()
        self.load_git_config()
        
    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.parent_frame, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.parent_frame.columnconfigure(0, weight=1)
        self.parent_frame.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        
        # Git仓库路径选择
        ttk.Label(main_frame, text="Git仓库路径:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.repo_combo = ttk.Combobox(main_frame, textvariable=self.repo_path, width=50)
        self.repo_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
        
        repo_btn_frame = ttk.Frame(main_frame)
        repo_btn_frame.grid(row=0, column=2, padx=5, pady=5)
        ttk.Button(repo_btn_frame, text="浏览", command=self.select_repo_folder).pack(side=tk.LEFT, padx=2)
        ttk.Button(repo_btn_frame, text="初始化Git", command=self.init_git_repo).pack(side=tk.LEFT, padx=2)
        
        # 远程仓库URL
        ttk.Label(main_frame, text="远程仓库URL:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.remote_entry = ttk.Entry(main_frame, textvariable=self.remote_url, width=50)
        self.remote_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
        
        remote_btn_frame = ttk.Frame(main_frame)
        remote_btn_frame.grid(row=1, column=2, padx=5, pady=5)
        ttk.Button(remote_btn_frame, text="设置远程", command=self.set_remote_url).pack(side=tk.LEFT, padx=2)
        ttk.Button(remote_btn_frame, text="测试连接", command=self.test_remote_connection).pack(side=tk.LEFT, padx=2)
        
        # Git配置
        git_config_frame = ttk.LabelFrame(main_frame, text="Git配置", padding="5")
        git_config_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        git_config_frame.columnconfigure(1, weight=1)
        
        # 分支名称
        ttk.Label(git_config_frame, text="分支名称:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
        branch_frame = ttk.Frame(git_config_frame)
        branch_frame.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)
        
        self.branch_combo = ttk.Combobox(branch_frame, textvariable=self.branch_name, width=18)
        self.branch_combo.pack(side=tk.LEFT, padx=(0, 5))
        
        ttk.Button(branch_frame, text="刷新分支", command=self.refresh_branches).pack(side=tk.LEFT)
        
        # 提交信息
        ttk.Label(git_config_frame, text="提交信息:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=2)
        ttk.Entry(git_config_frame, textvariable=self.commit_message, width=40).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=5, pady=2)
        
        # 同步选项
        sync_options_frame = ttk.LabelFrame(main_frame, text="同步选项", padding="5")
        sync_options_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        sync_options_frame.columnconfigure(1, weight=1)
        
        # 自动提交
        ttk.Checkbutton(sync_options_frame, text="自动提交变更", variable=self.auto_commit).grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
        
        # 自动推送
        ttk.Checkbutton(sync_options_frame, text="自动推送到远程", variable=self.auto_push).grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)
        
        # 同步间隔
        ttk.Label(sync_options_frame, text="同步间隔(秒):").grid(row=1, column=0, sticky=tk.W, padx=5, pady=2)
        interval_frame = ttk.Frame(sync_options_frame)
        interval_frame.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2)
        
        # 30秒步进，最长30分钟(1800秒)
        self.interval_scale = ttk.Scale(interval_frame, from_=30, to=1800, variable=self.sync_interval, 
                                       orient=tk.HORIZONTAL, length=200, command=self.on_interval_change)
        self.interval_scale.pack(side=tk.LEFT)
        
        # 显示格式化的时间
        self.interval_display = tk.StringVar()
        self.update_interval_display()
        ttk.Label(interval_frame, textvariable=self.interval_display).pack(side=tk.LEFT, padx=5)
        
        # 控制按钮
        git_button_frame = ttk.Frame(main_frame)
        git_button_frame.grid(row=4, column=0, columnspan=3, pady=20)
        
        self.git_start_button = ttk.Button(git_button_frame, text="开始Git同步", command=self.start_git_sync)
        self.git_start_button.pack(side=tk.LEFT, padx=5)
        
        self.git_stop_button = ttk.Button(git_button_frame, text="停止Git同步", command=self.stop_git_sync, state=tk.DISABLED)
        self.git_stop_button.pack(side=tk.LEFT, padx=5)
        
        self.manual_commit_button = ttk.Button(git_button_frame, text="手动提交", command=self.manual_commit)
        self.manual_commit_button.pack(side=tk.LEFT, padx=5)
        
        self.manual_push_button = ttk.Button(git_button_frame, text="手动推送", command=self.manual_push)
        self.manual_push_button.pack(side=tk.LEFT, padx=5)
        
        self.git_status_button = ttk.Button(git_button_frame, text="查看状态", command=self.show_git_status)
        self.git_status_button.pack(side=tk.LEFT, padx=5)
        
        # Git进度显示
        git_progress_frame = ttk.Frame(main_frame)
        git_progress_frame.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        git_progress_frame.columnconfigure(1, weight=1)
        
        ttk.Label(git_progress_frame, text="Git状态:").grid(row=0, column=0, sticky=tk.W, padx=5)
        self.git_status_var = tk.StringVar(value="就绪")
        self.git_status_label = ttk.Label(git_progress_frame, textvariable=self.git_status_var)
        self.git_status_label.grid(row=0, column=1, sticky=tk.W, padx=5)
        
        # Git日志显示
        ttk.Label(main_frame, text="Git操作日志:").grid(row=6, column=0, sticky=(tk.W, tk.N), pady=5)
        
        git_log_frame = ttk.Frame(main_frame)
        git_log_frame.grid(row=6, column=1, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
        git_log_frame.columnconfigure(0, weight=1)
        git_log_frame.rowconfigure(0, weight=1)
        
        self.git_log_text = tk.Text(git_log_frame, height=15, wrap=tk.WORD)
        git_log_scrollbar = ttk.Scrollbar(git_log_frame, orient=tk.VERTICAL, command=self.git_log_text.yview)
        self.git_log_text.configure(yscrollcommand=git_log_scrollbar.set)
        
        self.git_log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        git_log_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        
        # 配置主框架的行权重
        main_frame.rowconfigure(6, weight=1)
    
    def on_interval_change(self, value):
        """处理同步间隔滑块变化"""
        # 将值调整为30秒的倍数
        interval = int(float(value))
        adjusted_interval = round(interval / 30) * 30
        
        # 确保在有效范围内
        adjusted_interval = max(30, min(1800, adjusted_interval))
        
        # 更新变量值
        self.sync_interval.set(adjusted_interval)
        
        # 更新显示
        self.update_interval_display()
        
        # 保存配置
        self.save_git_config()
    
    def update_interval_display(self):
        """更新时间间隔的显示格式"""
        interval = self.sync_interval.get()
        
        if interval < 60:
            # 小于1分钟，显示秒数
            display_text = f"{interval}秒"
        elif interval < 3600:
            # 小于1小时，显示分钟和秒数
            minutes = interval // 60
            seconds = interval % 60
            if seconds == 0:
                display_text = f"{minutes}分钟"
            else:
                display_text = f"{minutes}分{seconds}秒"
        else:
            # 1小时或以上，显示小时、分钟和秒数
            hours = interval // 3600
            remaining = interval % 3600
            minutes = remaining // 60
            seconds = remaining % 60
            
            parts = [f"{hours}小时"]
            if minutes > 0:
                parts.append(f"{minutes}分钟")
            if seconds > 0:
                parts.append(f"{seconds}秒")
            display_text = "".join(parts)
        
        self.interval_display.set(display_text)
    
    def select_repo_folder(self):
        """选择Git仓库文件夹"""
        folder = filedialog.askdirectory(title="选择Git仓库文件夹")
        if folder:
            self.repo_path.set(folder)
            # 自动获取并显示远程仓库URL
            self.load_remote_url()
            self.save_git_config()
            self.log_git_message(f"选择仓库路径: {folder}")
    
    def init_git_repo(self):
        """初始化Git仓库"""
        repo_path = self.repo_path.get().strip()
        if not repo_path:
            messagebox.showerror("错误", "请先选择仓库路径")
            return
        
        try:
            import git
            if os.path.exists(os.path.join(repo_path, '.git')):
                self.log_git_message(f"仓库已存在: {repo_path}")
                messagebox.showinfo("信息", "该文件夹已经是Git仓库")
            else:
                repo = git.Repo.init(repo_path)
                self.log_git_message(f"初始化Git仓库: {repo_path}")
                messagebox.showinfo("成功", "Git仓库初始化成功")
        except ImportError:
            messagebox.showerror("错误", "请先安装GitPython库: pip install GitPython")
        except Exception as e:
            self.log_git_message(f"初始化仓库失败: {str(e)}")
            messagebox.showerror("错误", f"初始化Git仓库失败: {str(e)}")
    
    def load_remote_url(self):
        """从Git仓库加载远程URL"""
        repo_path = self.repo_path.get().strip()
        
        if not repo_path:
            return
        
        try:
            import git
            
            # 检查是否是有效的Git仓库
            if not os.path.exists(os.path.join(repo_path, '.git')):
                self.remote_url.set("")  # 清空远程URL显示
                self.branch_combo['values'] = []  # 清空分支列表
                return
            
            repo = git.Repo(repo_path)
            
            # 获取origin远程的URL
            if repo.remotes and 'origin' in [remote.name for remote in repo.remotes]:
                origin_url = repo.remotes.origin.url
                self.remote_url.set(origin_url)
                self.log_git_message(f"检测到远程仓库: {origin_url}")
            else:
                self.remote_url.set("")  # 清空远程URL显示
                self.log_git_message("未检测到远程仓库配置")
            
            # 自动刷新分支列表
            self.refresh_branches()
                
        except ImportError:
            self.log_git_message("GitPython库未安装，无法检测远程仓库")
        except Exception as e:
            self.log_git_message(f"检测远程仓库失败: {str(e)}")
            self.remote_url.set("")  # 清空远程URL显示
            self.branch_combo['values'] = []  # 清空分支列表
    
    def refresh_branches(self):
        """刷新Git分支列表"""
        repo_path = self.repo_path.get().strip()
        
        if not repo_path:
            self.branch_combo['values'] = []
            return
        
        try:
            import git
            
            # 检查是否是有效的Git仓库
            if not os.path.exists(os.path.join(repo_path, '.git')):
                self.branch_combo['values'] = []
                self.log_git_message("不是有效的Git仓库，无法获取分支列表")
                return
            
            repo = git.Repo(repo_path)
            
            # 获取所有本地分支
            local_branches = [branch.name for branch in repo.branches]
            
            # 获取所有远程分支（如果有的话）
            remote_branches = []
            if repo.remotes:
                try:
                    for remote in repo.remotes:
                        for ref in remote.refs:
                            branch_name = ref.name.split('/')[-1]  # 获取分支名（去掉remote/前缀）
                            if branch_name not in ['HEAD'] and branch_name not in local_branches:
                                remote_branches.append(f"{remote.name}/{branch_name}")
                except Exception:
                    pass  # 忽略远程分支获取错误
            
            # 合并本地和远程分支
            all_branches = local_branches + remote_branches
            
            # 更新下拉框选项
            self.branch_combo['values'] = all_branches
            
            # 如果当前没有选择分支，自动选择当前活跃分支
            if not self.branch_name.get() and repo.active_branch:
                current_branch = repo.active_branch.name
                self.branch_name.set(current_branch)
                self.log_git_message(f"当前分支: {current_branch}")
            
            if all_branches:
                self.log_git_message(f"检测到 {len(local_branches)} 个本地分支，{len(remote_branches)} 个远程分支")
            else:
                self.log_git_message("未检测到任何分支")
                
        except ImportError:
            self.log_git_message("GitPython库未安装，无法获取分支列表")
            self.branch_combo['values'] = []
        except Exception as e:
            self.log_git_message(f"获取分支列表失败: {str(e)}")
            self.branch_combo['values'] = []
    
    def set_remote_url(self):
        """设置远程仓库URL"""
        repo_path = self.repo_path.get().strip()
        remote_url = self.remote_url.get().strip()
        
        if not repo_path or not remote_url:
            messagebox.showerror("错误", "请先设置仓库路径和远程URL")
            return
        
        try:
            import git
            repo = git.Repo(repo_path)
            
            # 检查是否已有origin远程
            if 'origin' in [remote.name for remote in repo.remotes]:
                repo.delete_remote('origin')
            
            # 添加新的origin远程
            repo.create_remote('origin', remote_url)
            self.save_git_config()
            self.log_git_message(f"设置远程仓库: {remote_url}")
            messagebox.showinfo("成功", "远程仓库设置成功")
        except ImportError:
            messagebox.showerror("错误", "请先安装GitPython库: pip install GitPython")
        except Exception as e:
            self.log_git_message(f"设置远程仓库失败: {str(e)}")
            messagebox.showerror("错误", f"设置远程仓库失败: {str(e)}")
    
    def test_remote_connection(self):
        """测试远程仓库连接"""
        repo_path = self.repo_path.get().strip()
        
        if not repo_path:
            messagebox.showerror("错误", "请先选择仓库路径")
            return
        
        try:
            import git
            
            # 首先检查是否是有效的Git仓库
            if not os.path.exists(os.path.join(repo_path, '.git')):
                messagebox.showerror("错误", "选择的路径不是Git仓库")
                return
            
            repo = git.Repo(repo_path)
            
            if not repo.remotes:
                messagebox.showwarning("警告", "没有配置远程仓库")
                return
            
            # 测试连接 - 使用与auto_push_changes相同的方法
            origin = repo.remotes.origin
            
            try:
                # 使用ls-remote检查远程仓库连接，这是最轻量级的方法
                origin.repo.git.ls_remote('--heads', origin.url)
                self.log_git_message("远程仓库连接测试成功")
                messagebox.showinfo("成功", "远程仓库连接正常")
            except git.exc.GitCommandError as e:
                error_msg = str(e).lower()
                if "could not resolve hostname" in error_msg or "无法解析主机名" in error_msg:
                    self.log_git_message("远程仓库连接失败: 无法解析主机名，请检查网络连接")
                    messagebox.showerror("连接失败", "无法解析主机名，请检查网络连接和远程仓库地址")
                elif "network is unreachable" in error_msg or "网络不可达" in error_msg:
                    self.log_git_message("远程仓库连接失败: 网络不可达")
                    messagebox.showerror("连接失败", "网络不可达，请检查网络连接")
                elif "authentication failed" in error_msg or "认证失败" in error_msg:
                    self.log_git_message("远程仓库连接失败: 认证失败")
                    messagebox.showerror("连接失败", "认证失败，请检查用户名和密码或SSH密钥")
                elif "permission denied" in error_msg or "权限拒绝" in error_msg:
                    self.log_git_message("远程仓库连接失败: 权限拒绝")
                    messagebox.showerror("连接失败", "权限拒绝，请检查仓库访问权限")
                elif "repository not found" in error_msg or "仓库未找到" in error_msg:
                    self.log_git_message("远程仓库连接失败: 仓库不存在")
                    messagebox.showerror("连接失败", "远程仓库不存在，请检查仓库地址")
                else:
                    self.log_git_message(f"远程仓库连接失败: {str(e)}")
                    messagebox.showerror("连接失败", f"远程仓库连接失败: {str(e)}")
                    
        except ImportError:
            messagebox.showerror("错误", "请先安装GitPython库: pip install GitPython")
        except Exception as e:
            self.log_git_message(f"远程仓库连接测试失败: {str(e)}")
            messagebox.showerror("错误", f"远程仓库连接失败: {str(e)}")
    
    def start_git_sync(self):
        """开始Git自动同步"""
        repo_path = self.repo_path.get().strip()
        
        if not repo_path:
            messagebox.showerror("错误", "请先选择仓库路径")
            return
        
        if not os.path.exists(os.path.join(repo_path, '.git')):
            messagebox.showerror("错误", "选择的路径不是Git仓库")
            return
        
        try:
            self.is_git_syncing = True
            self.git_start_button.config(state=tk.DISABLED)
            self.git_stop_button.config(state=tk.NORMAL)
            
            # 启动文件监控
            self.start_git_file_monitor()
            
            # 启动定时同步
            self.schedule_git_sync()
            
            self.git_status_var.set("同步中...")
            self.log_git_message("开始Git自动同步")
            messagebox.showinfo("成功", "Git自动同步已启动")
        except Exception as e:
            self.log_git_message(f"启动Git同步失败: {str(e)}")
            messagebox.showerror("错误", f"启动Git同步失败: {str(e)}")
    
    def stop_git_sync(self):
        """停止Git自动同步"""
        try:
            self.is_git_syncing = False
            
            # 停止文件监控
            if self.git_observer:
                self.git_observer.stop()
                self.git_observer.join()
                self.git_observer = None
            
            # 停止定时器
            if self.git_sync_timer:
                self.git_sync_timer.cancel()
                self.git_sync_timer = None
            
            self.git_start_button.config(state=tk.NORMAL)
            self.git_stop_button.config(state=tk.DISABLED)
            self.git_status_var.set("已停止")
            self.log_git_message("停止Git自动同步")
            messagebox.showinfo("成功", "Git自动同步已停止")
        except Exception as e:
            self.log_git_message(f"停止Git同步失败: {str(e)}")
            messagebox.showerror("错误", f"停止Git同步失败: {str(e)}")
    
    def start_git_file_monitor(self):
        """启动Git文件监控"""
        repo_path = self.repo_path.get().strip()
        
        try:
            from watchdog.observers import Observer
            from watchdog.events import FileSystemEventHandler
            
            class GitFileHandler(FileSystemEventHandler):
                def __init__(self, git_sync_tab):
                    self.git_sync_tab = git_sync_tab
                    self.last_event_time = 0
                    self.debounce_delay = 2  # 防抖延迟（秒）
                
                def on_any_event(self, event):
                    if not self.git_sync_tab.is_git_syncing:
                        return
                    
                    # 忽略.git文件夹的变化
                    if '.git' in event.src_path:
                        return
                    
                    # 忽略临时文件和系统文件
                    if any(pattern in event.src_path for pattern in ['.tmp', '.temp', '~', '.swp', '.lock']):
                        return
                    
                    # 防抖处理
                    current_time = time.time()
                    if current_time - self.last_event_time < self.debounce_delay:
                        return
                    
                    self.last_event_time = current_time
                    
                    if self.git_sync_tab.auto_commit.get():
                        # 延迟执行，避免频繁提交，并在提交前再次检查是否有变化
                        threading.Timer(self.debounce_delay, self.git_sync_tab.auto_commit_with_check).start()
            
            self.git_observer = Observer()
            event_handler = GitFileHandler(self)
            self.git_observer.schedule(event_handler, repo_path, recursive=True)
            self.git_observer.start()
            
            self.log_git_message(f"开始监控文件变化: {repo_path}")
        except ImportError:
            messagebox.showerror("错误", "请先安装watchdog库: pip install watchdog")
        except Exception as e:
            self.log_git_message(f"启动文件监控失败: {str(e)}")
    
    def schedule_git_sync(self):
        """定时Git同步"""
        if not self.is_git_syncing:
            return
        
        try:
            # 先检查是否需要自动提交
            if self.auto_commit.get():
                self.auto_commit_changes()
            
            # 再检查是否需要自动推送
            if self.auto_push.get():
                self.auto_push_changes()
            
            # 安排下次同步
            interval = self.sync_interval.get()
            self.git_sync_timer = threading.Timer(interval, self.schedule_git_sync)
            self.git_sync_timer.start()
        except Exception as e:
            self.log_git_message(f"定时同步失败: {str(e)}")
    
    def auto_commit_with_check(self):
        """带检查的自动提交变更（用于文件监控触发）"""
        repo_path = self.repo_path.get().strip()
        
        try:
            import git
            repo = git.Repo(repo_path)
            
            # 检查是否有实际变更
            if repo.is_dirty() or repo.untracked_files:
                # 获取变更的文件列表
                modified_files = [item.a_path for item in repo.index.diff(None)]
                untracked_files = repo.untracked_files
                
                # 过滤掉临时文件和系统文件
                temp_patterns = ['.tmp', '.temp', '~', '.swp', '.lock', '.DS_Store', 'Thumbs.db']
                filtered_modified = [f for f in modified_files if not any(pattern in f for pattern in temp_patterns)]
                filtered_untracked = [f for f in untracked_files if not any(pattern in f for pattern in temp_patterns)]
                
                # 如果过滤后还有文件变更，才进行提交
                if filtered_modified or filtered_untracked:
                    self.auto_commit_changes()
                else:
                    self.log_git_message("检测到的变更为临时文件，跳过提交")
            else:
                self.log_git_message("没有变更需要提交")
        except ImportError:
            self.log_git_message("错误: 请先安装GitPython库")
        except Exception as e:
            self.log_git_message(f"检查提交失败: {str(e)}")
    
    def auto_commit_changes(self):
        """自动提交变更"""
        repo_path = self.repo_path.get().strip()
        
        try:
            import git
            repo = git.Repo(repo_path)
            
            # 检查是否有变更
            if repo.is_dirty() or repo.untracked_files:
                # 添加所有变更
                repo.git.add(A=True)
                
                # 提交变更
                commit_msg = self.commit_message.get() or "自动同步"
                commit_msg += f" - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
                repo.index.commit(commit_msg)
                
                self.log_git_message(f"自动提交: {commit_msg}")
                self.git_status_var.set("已提交")
            else:
                self.log_git_message("没有变更需要提交")
        except ImportError:
            self.log_git_message("错误: 请先安装GitPython库")
        except Exception as e:
            self.log_git_message(f"自动提交失败: {str(e)}")
    
    def auto_push_changes(self):
        """自动推送变更"""
        repo_path = self.repo_path.get().strip()
        
        if not repo_path:
            self.log_git_message("错误: 仓库路径为空")
            return
        
        try:
            import git
            repo = git.Repo(repo_path)
            
            if not repo.remotes:
                self.log_git_message("没有配置远程仓库，跳过推送")
                return
            
            # 检查仓库是否有提交
            try:
                repo.head.commit
            except ValueError:
                self.log_git_message("仓库没有任何提交，跳过推送")
                return
            except git.exc.InvalidGitRepositoryError:
                self.log_git_message("错误: 无效的Git仓库")
                return
            
            # 获取当前分支名
            current_branch = repo.active_branch.name
            config_branch = self.branch_name.get().strip()
            
            # 如果配置的分支名与当前分支不同，使用当前分支并更新配置
            if current_branch != config_branch and config_branch:
                self.branch_name.set(current_branch)
                self.save_git_config()
                self.log_git_message(f"已更新分支配置为当前分支: {current_branch}")
            
            branch_name = current_branch
            
            # 推送到远程
            origin = repo.remotes.origin
            
            # 检查远程仓库连接（使用轻量级方法）
            try:
                # 使用 ls-remote 命令检查远程仓库连接，这比 fetch 更轻量级
                origin.repo.git.ls_remote('--heads', origin.url)
            except git.exc.GitCommandError as e:
                if "Could not resolve hostname" in str(e) or "Network is unreachable" in str(e):
                    self.log_git_message("错误: 无法连接到远程仓库，请检查网络连接")
                    return
                elif "Authentication failed" in str(e) or "Permission denied" in str(e):
                    self.log_git_message("错误: 远程仓库认证失败，请检查凭据")
                    return
                elif "Repository not found" in str(e):
                    self.log_git_message("错误: 远程仓库不存在")
                    return
                # 其他错误继续执行，可能是正常的连接错误
            except Exception as e:
                # 对于首次推送的情况，跳过连接验证警告
                if "FETCH_HEAD" not in str(e):
                    self.log_git_message(f"警告: 无法验证远程连接: {str(e)}")
            
            # 检查是否有本地提交需要推送
            try:
                # 先尝试获取远程分支信息
                try:
                    # 尝试获取远程分支的引用
                    remote_ref = f'origin/{branch_name}'
                    if remote_ref in [ref.name for ref in repo.refs]:
                        # 远程分支存在，比较提交
                        commits_ahead = list(repo.iter_commits(f'{remote_ref}..{branch_name}'))
                        if commits_ahead:
                            origin.push(branch_name)
                            self.log_git_message(f"自动推送到远程分支: {branch_name} ({len(commits_ahead)} 个提交)")
                            self.git_status_var.set(f"已推送 - {datetime.now().strftime('%H:%M:%S')}")
                            # 记录最后推送时间
                            self.last_push_time = datetime.now()
                        else:
                            self.log_git_message("没有新提交需要推送")
                    else:
                        # 远程分支不存在，检查本地是否有提交后进行首次推送
                        local_commits = list(repo.iter_commits(branch_name))
                        if local_commits:
                            origin.push(branch_name, set_upstream=True)
                            self.log_git_message(f"首次推送到远程分支: {branch_name} ({len(local_commits)} 个提交)")
                            self.git_status_var.set(f"已推送 - {datetime.now().strftime('%H:%M:%S')}")
                            # 记录最后推送时间
                            self.last_push_time = datetime.now()
                        else:
                            self.log_git_message(f"本地分支 '{branch_name}' 没有提交，跳过推送")
                except git.exc.GitCommandError as e:
                    if "does not exist" in str(e) or "src refspec" in str(e):
                        # 远程分支不存在，检查本地提交后首次推送
                        try:
                            local_commits = list(repo.iter_commits(branch_name))
                            if local_commits:
                                origin.push(branch_name, set_upstream=True)
                                self.log_git_message(f"首次推送到远程分支: {branch_name} ({len(local_commits)} 个提交)")
                                self.git_status_var.set(f"已推送 - {datetime.now().strftime('%H:%M:%S')}")
                                # 记录最后推送时间
                                self.last_push_time = datetime.now()
                            else:
                                self.log_git_message(f"本地分支 '{branch_name}' 没有提交，跳过推送")
                        except git.exc.GitCommandError as push_error:
                            if "src refspec" in str(push_error) and "does not match any" in str(push_error):
                                self.log_git_message(f"错误: 本地分支 '{branch_name}' 不存在")
                            else:
                                raise push_error
                    else:
                        raise e
            except git.exc.GitCommandError as e:
                # 处理其他Git命令错误
                if "src refspec" in str(e) and "does not match any" in str(e):
                    self.log_git_message(f"错误: 本地分支 '{branch_name}' 不存在或没有提交")
                elif "failed to push some refs" in str(e):
                    self.log_git_message(f"推送失败: 可能需要先拉取远程更新")
                    self.log_git_message("建议: 请手动执行 git pull 后再推送")
                else:
                    self.log_git_message(f"推送失败: {str(e)}")
        except ImportError:
            self.log_git_message("错误: 请先安装GitPython库")
        except Exception as e:
            self.log_git_message(f"自动推送失败: {str(e)}")
    
    def manual_commit(self):
        """手动提交"""
        self.auto_commit_changes()
    
    def manual_push(self):
        """手动推送"""
        self.auto_push_changes()
    
    def show_git_status(self):
        """显示Git状态"""
        repo_path = self.repo_path.get().strip()
        
        if not repo_path:
            messagebox.showerror("错误", "请先选择仓库路径")
            return
        
        try:
            import git
            repo = git.Repo(repo_path)
            
            status_info = []
            status_info.append(f"仓库路径: {repo_path}")
            status_info.append(f"当前分支: {repo.active_branch.name}")
            
            # 检查工作区状态
            if repo.is_dirty():
                status_info.append("工作区状态: 有未提交的变更")
                modified_files = [item.a_path for item in repo.index.diff(None)]
                if modified_files:
                    status_info.append(f"修改的文件: {', '.join(modified_files[:5])}")
                    if len(modified_files) > 5:
                        status_info.append(f"... 还有 {len(modified_files) - 5} 个文件")
            else:
                status_info.append("工作区状态: 干净")
            
            # 检查未跟踪文件
            if repo.untracked_files:
                status_info.append(f"未跟踪文件: {len(repo.untracked_files)} 个")
            
            # 检查远程状态
            if repo.remotes:
                try:
                    origin = repo.remotes.origin
                    branch_name = repo.active_branch.name
                    commits_ahead = list(repo.iter_commits(f'origin/{branch_name}..{branch_name}'))
                    commits_behind = list(repo.iter_commits(f'{branch_name}..origin/{branch_name}'))
                    
                    if commits_ahead:
                        status_info.append(f"领先远程: {len(commits_ahead)} 个提交")
                    if commits_behind:
                        status_info.append(f"落后远程: {len(commits_behind)} 个提交")
                    if not commits_ahead and not commits_behind:
                        status_info.append("与远程同步")
                except:
                    status_info.append("无法获取远程状态")
            else:
                status_info.append("没有配置远程仓库")
            
            messagebox.showinfo("Git状态", "\n".join(status_info))
        except ImportError:
            messagebox.showerror("错误", "请先安装GitPython库: pip install GitPython")
        except Exception as e:
            messagebox.showerror("错误", f"获取Git状态失败: {str(e)}")
    
    def log_git_message(self, message):
        """记录Git操作日志"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        log_entry = f"[{timestamp}] {message}\n"
        
        self.git_log_text.insert(tk.END, log_entry)
        self.git_log_text.see(tk.END)
        
        # 限制日志长度
        lines = self.git_log_text.get("1.0", tk.END).split("\n")
        if len(lines) > 1000:
            self.git_log_text.delete("1.0", f"{len(lines) - 500}.0")
    
    def save_git_config(self):
        """保存Git配置"""
        try:
            config = {
                "repo_path": self.repo_path.get(),
                "remote_url": self.remote_url.get(),
                "branch_name": self.branch_name.get(),
                "commit_message": self.commit_message.get(),
                "auto_commit": self.auto_commit.get(),
                "auto_push": self.auto_push.get(),
                "sync_interval": self.sync_interval.get()
            }
            
            with open(self.git_config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
        except Exception as e:
            self.log_git_message(f"保存配置失败: {str(e)}")
    
    def load_git_config(self):
        """加载Git配置"""
        try:
            if os.path.exists(self.git_config_file):
                with open(self.git_config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                
                self.repo_path.set(config.get("repo_path", ""))
                self.remote_url.set(config.get("remote_url", ""))
                self.branch_name.set(config.get("branch_name", "main"))
                self.commit_message.set(config.get("commit_message", "自动同步"))
                self.auto_commit.set(config.get("auto_commit", True))
                self.auto_push.set(config.get("auto_push", False))
                
                # 确保同步间隔符合30秒步进要求
                interval = config.get("sync_interval", 300)
                adjusted_interval = max(30, min(1800, round(interval / 30) * 30))
                self.sync_interval.set(adjusted_interval)
                
                # 更新时间显示
                self.update_interval_display()
                
                # 更新下拉框历史
                if self.repo_path.get():
                    self.repo_combo['values'] = [self.repo_path.get()]
                    # 如果有仓库路径，尝试自动获取远程URL
                    self.load_remote_url()
        except Exception as e:
            self.log_git_message(f"加载配置失败: {str(e)}")

class FileSyncTab:
    def __init__(self, parent_frame):
        self.parent_frame = parent_frame
        
        # 变量
        self.source_folder = tk.StringVar()
        self.target_folder = tk.StringVar()
        self.is_syncing = False
        self.observer = None
        self.observer_target = None  # 双向同步时的目标文件夹监控器
        self.sync_mode = tk.StringVar(value="单向同步")  # 同步模式：单向同步/双向同步
        self.conflict_resolution = tk.StringVar(value="较新文件优先")  # 冲突解决策略
        self.global_sync_lock = threading.Lock()  # 全局同步锁，防止循环同步
        self.history_file = get_resource_path("folder_history.json")
        self.exclude_rules_file = get_resource_path("exclude_rules.json")
        self.folder_history = self.load_history()
        self.different_files = []
        self.exclude_rules = []  # 排除规则列表
        
        self.setup_ui()
        
        # 加载排除规则（在UI创建后）
        self.load_default_exclude_rules()
        
    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.parent_frame, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.parent_frame.columnconfigure(0, weight=1)
        self.parent_frame.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        
        # 源文件夹选择
        ttk.Label(main_frame, text="源文件夹 A:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.source_combo = ttk.Combobox(main_frame, textvariable=self.source_folder, width=47)
        self.source_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
        self.source_combo['values'] = [item['source'] for item in self.folder_history]
        
        source_btn_frame = ttk.Frame(main_frame)
        source_btn_frame.grid(row=0, column=2, padx=5, pady=5)
        ttk.Button(source_btn_frame, text="浏览", command=self.select_source_folder).pack(side=tk.LEFT, padx=2)
        ttk.Button(source_btn_frame, text="清除此项", command=self.clear_source_history).pack(side=tk.LEFT, padx=2)
        
        # 目标文件夹选择
        ttk.Label(main_frame, text="目标文件夹 B:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.target_combo = ttk.Combobox(main_frame, textvariable=self.target_folder, width=47)
        self.target_combo.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=5, pady=5)
        self.target_combo['values'] = [item['target'] for item in self.folder_history]
        
        target_btn_frame = ttk.Frame(main_frame)
        target_btn_frame.grid(row=1, column=2, padx=5, pady=5)
        ttk.Button(target_btn_frame, text="浏览", command=self.select_target_folder).pack(side=tk.LEFT, padx=2)
        ttk.Button(target_btn_frame, text="清除此项", command=self.clear_target_history).pack(side=tk.LEFT, padx=2)
        
        # 历史记录管理按钮
        history_frame = ttk.Frame(main_frame)
        history_frame.grid(row=2, column=0, columnspan=3, pady=5)
        
        ttk.Button(history_frame, text="管理历史记录", command=self.show_history_manager).pack(side=tk.LEFT, padx=5)
        ttk.Button(history_frame, text="清空所有历史", command=self.clear_all_history).pack(side=tk.LEFT, padx=5)
        
        # 同步模式配置
        sync_config_frame = ttk.LabelFrame(main_frame, text="同步配置", padding="5")
        sync_config_frame.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        sync_config_frame.columnconfigure(1, weight=1)
        
        # 同步模式选择
        ttk.Label(sync_config_frame, text="同步模式:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
        sync_mode_frame = ttk.Frame(sync_config_frame)
        sync_mode_frame.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)
        
        ttk.Radiobutton(sync_mode_frame, text="单向同步 (A → B)", variable=self.sync_mode, 
                       value="单向同步", command=self.on_sync_mode_changed).pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(sync_mode_frame, text="双向同步 (A ↔ B)", variable=self.sync_mode, 
                       value="双向同步", command=self.on_sync_mode_changed).pack(side=tk.LEFT, padx=5)
        
        # 冲突解决策略（仅在双向同步时显示）
        ttk.Label(sync_config_frame, text="冲突解决:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=2)
        self.conflict_frame = ttk.Frame(sync_config_frame)
        self.conflict_frame.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2)
        
        ttk.Radiobutton(self.conflict_frame, text="较新文件优先", variable=self.conflict_resolution, 
                       value="较新文件优先").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(self.conflict_frame, text="较大文件优先", variable=self.conflict_resolution, 
                       value="较大文件优先").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(self.conflict_frame, text="A文件夹优先", variable=self.conflict_resolution, 
                       value="A文件夹优先").pack(side=tk.LEFT, padx=5)
        
        # 初始状态下隐藏冲突解决选项
        self.toggle_conflict_options()
        
        # 绑定下拉框选择事件
        self.source_combo.bind('<<ComboboxSelected>>', self.on_source_selected)
        self.target_combo.bind('<<ComboboxSelected>>', self.on_target_selected)
        
        # 排除规则配置
        exclude_frame = ttk.LabelFrame(main_frame, text="排除规则配置", padding="5")
        exclude_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10)
        exclude_frame.columnconfigure(1, weight=1)
        
        ttk.Label(exclude_frame, text="规则:").grid(row=0, column=0, sticky=tk.W, padx=5)
        self.exclude_entry = ttk.Entry(exclude_frame, width=40)
        self.exclude_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5)
        
        exclude_btn_frame = ttk.Frame(exclude_frame)
        exclude_btn_frame.grid(row=0, column=2, padx=5)
        
        ttk.Button(exclude_btn_frame, text="添加", command=self.add_exclude_rule).pack(side=tk.LEFT, padx=2)
        ttk.Button(exclude_btn_frame, text="删除", command=self.remove_exclude_rule).pack(side=tk.LEFT, padx=2)
        
        # 排除规则列表
        ttk.Label(exclude_frame, text="当前规则:").grid(row=1, column=0, sticky=(tk.W, tk.N), padx=5, pady=5)
        
        rules_frame = ttk.Frame(exclude_frame)
        rules_frame.grid(row=1, column=1, columnspan=2, sticky=(tk.W, tk.E), padx=5, pady=5)
        rules_frame.columnconfigure(0, weight=1)
        
        self.rules_listbox = tk.Listbox(rules_frame, height=4)
        rules_scrollbar = ttk.Scrollbar(rules_frame, orient=tk.VERTICAL, command=self.rules_listbox.yview)
        self.rules_listbox.configure(yscrollcommand=rules_scrollbar.set)
        
        self.rules_listbox.grid(row=0, column=0, sticky=(tk.W, tk.E))
        rules_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        
        # 添加排除规则说明
        help_text = (
            "排除规则说明:\n"
            "• 文件规则: *.tmp, *.log, temp.txt\n"
            "• 目录规则: __pycache__/*, .git/*, node_modules/*\n"
            "• 支持通配符: * (任意字符) 和 ? (单个字符)\n"
            "• 目录规则以 /* 结尾，会排除整个目录及其内容"
        )
        help_label = ttk.Label(exclude_frame, text=help_text, font=('Arial', 8), foreground='#666666', justify=tk.LEFT)
        help_label.grid(row=2, column=0, columnspan=3, sticky=tk.W, padx=5, pady=5)
        
        # 控制按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=5, column=0, columnspan=3, pady=20)
        
        self.start_button = ttk.Button(button_frame, text="开始同步", command=self.start_sync)
        self.start_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = ttk.Button(button_frame, text="停止同步", command=self.stop_sync, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        
        self.compare_button = ttk.Button(button_frame, text="对比文件夹", command=self.compare_folders)
        self.compare_button.pack(side=tk.LEFT, padx=5)
        
        self.diff_list_button = ttk.Button(button_frame, text="查看差异列表", command=self.show_diff_list, state=tk.DISABLED)
        self.diff_list_button.pack(side=tk.LEFT, padx=5)
        
        # 进度条
        progress_frame = ttk.Frame(main_frame)
        progress_frame.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        progress_frame.columnconfigure(1, weight=1)
        
        ttk.Label(progress_frame, text="进度:").grid(row=0, column=0, sticky=tk.W, padx=5)
        self.progress_var = tk.StringVar(value="就绪")
        self.progress_label = ttk.Label(progress_frame, textvariable=self.progress_var)
        self.progress_label.grid(row=0, column=1, sticky=tk.W, padx=5)
        
        self.progress_bar = ttk.Progressbar(progress_frame, mode='determinate')
        self.progress_bar.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
        
        # 状态显示
        ttk.Label(main_frame, text="同步状态:").grid(row=7, column=0, sticky=(tk.W, tk.N), pady=5)
        
        # 状态文本框架
        status_frame = ttk.Frame(main_frame)
        status_frame.grid(row=7, column=1, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
        status_frame.columnconfigure(0, weight=1)
        status_frame.rowconfigure(0, weight=1)
        
        # 状态文本框和滚动条
        self.status_text = tk.Text(status_frame, height=10, wrap=tk.WORD)
        scrollbar = ttk.Scrollbar(status_frame, orient=tk.VERTICAL, command=self.status_text.yview)
        self.status_text.configure(yscrollcommand=scrollbar.set)
        
        self.status_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        
        # 配置主框架的行权重
        main_frame.rowconfigure(7, weight=1)
        
        # 初始状态消息
        self.update_status("就绪 - 请选择源文件夹和目标文件夹")
        
    def on_sync_mode_changed(self):
        """同步模式改变时的处理"""
        self.toggle_conflict_options()
        mode = self.sync_mode.get()
        if mode == "双向同步":
            self.update_status("已切换到双向同步模式 - 两个文件夹将保持同步")
        else:
            self.update_status("已切换到单向同步模式 - 仅从A同步到B")
            
    def toggle_conflict_options(self):
        """根据同步模式显示/隐藏冲突解决选项"""
        if self.sync_mode.get() == "双向同步":
            # 显示冲突解决选项
            for widget in self.conflict_frame.winfo_children():
                widget.configure(state='normal')
        else:
            # 隐藏冲突解决选项
            for widget in self.conflict_frame.winfo_children():
                widget.configure(state='disabled')
        
    def load_default_exclude_rules(self):
        """加载排除规则"""
        # 先尝试加载保存的规则
        if self.load_exclude_rules():
            return
            
        # 如果没有保存的规则，则加载默认规则
        default_rules = [
            "*.tmp",
            "*.log",
            "__pycache__/*",
            ".git/*",
            ".svn/*",
            "node_modules/*",
            "*.pyc",
            "Thumbs.db",
            ".DS_Store"
        ]
        
        for rule in default_rules:
            self.exclude_rules.append(rule)
            self.rules_listbox.insert(tk.END, rule)
            
    def load_exclude_rules(self):
        """从文件加载排除规则"""
        try:
            if os.path.exists(self.exclude_rules_file):
                with open(self.exclude_rules_file, 'r', encoding='utf-8') as f:
                    saved_rules = json.load(f)
                    for rule in saved_rules:
                        self.exclude_rules.append(rule)
                        self.rules_listbox.insert(tk.END, rule)
                    return True
        except Exception as e:
            self.update_status(f"加载排除规则失败: {str(e)}")
        return False
        
    def save_exclude_rules(self):
        """保存排除规则到文件"""
        try:
            with open(self.exclude_rules_file, 'w', encoding='utf-8') as f:
                json.dump(self.exclude_rules, f, ensure_ascii=False, indent=2)
        except Exception as e:
            self.update_status(f"保存排除规则失败: {str(e)}")
            
    def add_exclude_rule(self):
        """添加排除规则"""
        rule = self.exclude_entry.get().strip()
        if rule and rule not in self.exclude_rules:
            self.exclude_rules.append(rule)
            self.rules_listbox.insert(tk.END, rule)
            self.exclude_entry.delete(0, tk.END)
            self.save_exclude_rules()  # 保存规则
            self.update_status(f"已添加排除规则: {rule}")
        elif rule in self.exclude_rules:
            messagebox.showwarning("警告", "该规则已存在")
        else:
            messagebox.showwarning("警告", "请输入有效的排除规则")
            
    def remove_exclude_rule(self):
        """删除选中的排除规则"""
        selection = self.rules_listbox.curselection()
        if selection:
            index = selection[0]
            rule = self.exclude_rules[index]
            self.exclude_rules.pop(index)
            self.rules_listbox.delete(index)
            self.save_exclude_rules()  # 保存规则
            self.update_status(f"已删除排除规则: {rule}")
        else:
            messagebox.showwarning("警告", "请先选择要删除的规则")
            
    def should_exclude_file(self, file_path):
        """检查文件是否应该被排除"""
        import fnmatch
        
        # 转换为相对路径字符串
        if isinstance(file_path, Path):
            path_str = str(file_path).replace('\\', '/')
        else:
            path_str = str(file_path).replace('\\', '/')
            
        # 检查每个排除规则
        for rule in self.exclude_rules:
            # 处理目录规则（以/*结尾）
            if rule.endswith('/*'):
                dir_pattern = rule[:-2]  # 移除/*
                # 检查文件是否在指定目录下
                if path_str.startswith(dir_pattern + '/') or path_str == dir_pattern:
                    return True
            # 处理文件规则
            elif fnmatch.fnmatch(path_str, rule) or fnmatch.fnmatch(Path(path_str).name, rule):
                return True
                
        return False
        
    def load_history(self):
        """加载文件夹历史记录"""
        try:
            if os.path.exists(self.history_file):
                with open(self.history_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
        except Exception:
            pass
        return []
        
    def save_history(self):
        """保存文件夹历史记录"""
        try:
            with open(self.history_file, 'w', encoding='utf-8') as f:
                json.dump(self.folder_history, f, ensure_ascii=False, indent=2)
        except Exception as e:
            self.update_status(f"保存历史记录失败: {str(e)}")
            
    def add_to_history(self, source, target):
        """添加到历史记录"""
        # 检查是否已存在相同的记录
        for item in self.folder_history:
            if item['source'] == source and item['target'] == target:
                return
                
        # 添加新记录
        self.folder_history.insert(0, {'source': source, 'target': target})
        
        # 限制历史记录数量
        if len(self.folder_history) > 10:
            self.folder_history = self.folder_history[:10]
            
        # 更新下拉框
        self.source_combo['values'] = [item['source'] for item in self.folder_history]
        self.target_combo['values'] = [item['target'] for item in self.folder_history]
        
        # 保存到文件
        self.save_history()
        
    def on_source_selected(self, event):
        """源文件夹下拉框选择事件"""
        selected_source = self.source_combo.get()
        # 查找对应的目标文件夹
        for item in self.folder_history:
            if item['source'] == selected_source:
                self.target_folder.set(item['target'])
                break
                
    def on_target_selected(self, event):
        """目标文件夹下拉框选择事件"""
        selected_target = self.target_combo.get()
        # 查找对应的源文件夹
        for item in self.folder_history:
            if item['target'] == selected_target:
                self.source_folder.set(item['source'])
                break
                
    def clear_source_history(self):
        """清除当前选中的源文件夹相关历史记录"""
        current_source = self.source_folder.get().strip()
        if not current_source:
            messagebox.showwarning("警告", "请先选择一个源文件夹")
            return
            
        # 统计要删除的记录数
        records_to_remove = [item for item in self.folder_history if item['source'] == current_source]
        if not records_to_remove:
            messagebox.showinfo("信息", "没有找到相关的历史记录")
            return
            
        if messagebox.askyesno("确认", f"确定要删除源文件夹 '{current_source}' 的 {len(records_to_remove)} 条相关历史记录吗？"):
            # 从历史记录中移除包含当前源文件夹的记录
            self.folder_history = [item for item in self.folder_history if item['source'] != current_source]
            
            # 更新下拉框
            self.source_combo['values'] = [item['source'] for item in self.folder_history]
            self.target_combo['values'] = [item['target'] for item in self.folder_history]
            
            # 清空当前选择
            self.source_folder.set('')
            self.target_folder.set('')
            
            # 保存历史记录
            self.save_history()
            
            self.update_status(f"已删除源文件夹 '{current_source}' 的 {len(records_to_remove)} 条相关历史记录")
            
    def clear_target_history(self):
        """清除当前选中的目标文件夹相关历史记录"""
        current_target = self.target_folder.get().strip()
        if not current_target:
            messagebox.showwarning("警告", "请先选择一个目标文件夹")
            return
            
        # 统计要删除的记录数
        records_to_remove = [item for item in self.folder_history if item['target'] == current_target]
        if not records_to_remove:
            messagebox.showinfo("信息", "没有找到相关的历史记录")
            return
            
        if messagebox.askyesno("确认", f"确定要删除目标文件夹 '{current_target}' 的 {len(records_to_remove)} 条相关历史记录吗？"):
            # 从历史记录中移除包含当前目标文件夹的记录
            self.folder_history = [item for item in self.folder_history if item['target'] != current_target]
            
            # 更新下拉框
            self.source_combo['values'] = [item['source'] for item in self.folder_history]
            self.target_combo['values'] = [item['target'] for item in self.folder_history]
            
            # 清空当前选择
            self.source_folder.set('')
            self.target_folder.set('')
            
            # 保存历史记录
            self.save_history()
            
            self.update_status(f"已删除目标文件夹 '{current_target}' 的 {len(records_to_remove)} 条相关历史记录")
            
    def clear_all_history(self):
        """清空所有历史记录"""
        if messagebox.askyesno("确认", "确定要清空所有历史记录吗？此操作不可恢复。"):
            self.folder_history = []
            self.source_combo['values'] = []
            self.target_combo['values'] = []
            self.source_folder.set('')
            self.target_folder.set('')
            self.save_history()
            self.update_status("已清空所有历史记录")
            
    def show_history_manager(self):
        """显示历史记录管理窗口"""
        if not self.folder_history:
            messagebox.showinfo("信息", "没有历史记录")
            return
            
        # 创建历史记录管理窗口
        history_window = tk.Toplevel(self.root)
        history_window.title("历史记录管理")
        history_window.geometry("700x500")
        history_window.resizable(True, True)
        # 设置窗口图标
        set_window_icon(history_window)
        
        # 创建主框架
        main_frame = ttk.Frame(history_window, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 标题
        title_label = ttk.Label(main_frame, text=f"历史记录管理 (共 {len(self.folder_history)} 条记录)", font=('Arial', 12, 'bold'))
        title_label.pack(pady=(0, 10))
        
        # 创建树形视图
        columns = ('序号', '源文件夹', '目标文件夹')
        tree = ttk.Treeview(main_frame, columns=columns, show='headings', height=15)
        
        # 定义列标题
        tree.heading('序号', text='序号')
        tree.heading('源文件夹', text='源文件夹')
        tree.heading('目标文件夹', text='目标文件夹')
        
        # 设置列宽
        tree.column('序号', width=60)
        tree.column('源文件夹', width=300)
        tree.column('目标文件夹', width=300)
        
        # 添加数据
        for i, item in enumerate(self.folder_history, 1):
            tree.insert('', tk.END, values=(i, item['source'], item['target']))
            
        # 添加滚动条
        tree_frame = ttk.Frame(main_frame)
        tree_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar_v = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=tree.yview)
        tree.configure(yscrollcommand=scrollbar_v.set)
        scrollbar_v.pack(side=tk.RIGHT, fill=tk.Y)
        
        tree_frame.pack_propagate(False)
        tree.pack(in_=tree_frame, fill=tk.BOTH, expand=True)
        scrollbar_v.pack(in_=tree_frame, side=tk.RIGHT, fill=tk.Y)
        
        # 按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=10)
        
        def remove_selected():
            """删除选中的历史记录"""
            selection = tree.selection()
            if not selection:
                messagebox.showwarning("警告", "请先选择要删除的记录")
                return
                
            if messagebox.askyesno("确认", f"确定要删除选中的 {len(selection)} 条记录吗？"):
                # 获取选中项的索引（从后往前删除，避免索引变化）
                indices = []
                for item in selection:
                    values = tree.item(item)['values']
                    indices.append(int(values[0]) - 1)  # 序号从1开始，索引从0开始
                
                # 从后往前删除
                for index in sorted(indices, reverse=True):
                    if 0 <= index < len(self.folder_history):
                        self.folder_history.pop(index)
                
                # 保存历史记录
                self.save_history()
                
                # 更新下拉框
                self.source_combo['values'] = [item['source'] for item in self.folder_history]
                self.target_combo['values'] = [item['target'] for item in self.folder_history]
                
                # 刷新树形视图
                tree.delete(*tree.get_children())
                for i, item in enumerate(self.folder_history, 1):
                    tree.insert('', tk.END, values=(i, item['source'], item['target']))
                
                # 更新标题
                title_label.config(text=f"历史记录管理 (共 {len(self.folder_history)} 条记录)")
                
                self.update_status(f"已删除 {len(selection)} 条历史记录")
        
        def clear_all():
            """清空所有历史记录"""
            if messagebox.askyesno("确认", "确定要清空所有历史记录吗？此操作不可恢复。"):
                self.folder_history = []
                self.save_history()
                
                # 更新下拉框
                self.source_combo['values'] = []
                self.target_combo['values'] = []
                self.source_folder.set('')
                self.target_folder.set('')
                
                # 关闭窗口
                history_window.destroy()
                
                self.update_status("已清空所有历史记录")
        
        # 按钮
        ttk.Button(button_frame, text="删除选中", command=remove_selected).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="清空所有", command=clear_all).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="关闭", command=history_window.destroy).pack(side=tk.RIGHT, padx=5)
        
        # 说明文字
        help_label = ttk.Label(main_frame, text="提示：选择一条或多条记录，然后点击'删除选中'按钮可以删除指定记录", 
                              font=('Arial', 9), foreground='#666666')
        help_label.pack(pady=5)
        
    def select_source_folder(self):
        folder = filedialog.askdirectory(title="选择源文件夹 A")
        if folder:
            self.source_folder.set(folder)
            self.update_status(f"已选择源文件夹: {folder}")
            
    def select_target_folder(self):
        folder = filedialog.askdirectory(title="选择目标文件夹 B")
        if folder:
            self.target_folder.set(folder)
            self.update_status(f"已选择目标文件夹: {folder}")
            
    def show_diff_list(self):
        """显示差异文件列表窗口"""
        if not self.different_files:
            messagebox.showinfo("信息", "没有发现差异文件")
            return
            
        # 创建新窗口
        diff_window = tk.Toplevel(self.root)
        diff_window.title("差异文件列表")
        diff_window.geometry("800x600")
        diff_window.resizable(True, True)
        # 设置窗口图标
        set_window_icon(diff_window)
        
        # 创建框架
        main_frame = ttk.Frame(diff_window, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 标题
        title_label = ttk.Label(main_frame, text=f"发现 {len(self.different_files)} 个差异文件", font=('Arial', 12, 'bold'))
        title_label.pack(pady=(0, 10))
        
        # 创建树形视图
        columns = ('文件路径', '差异类型', '详细信息')
        tree = ttk.Treeview(diff_window, columns=columns, show='headings', height=20)
        
        # 定义列标题
        tree.heading('文件路径', text='文件路径')
        tree.heading('差异类型', text='差异类型')
        tree.heading('详细信息', text='详细信息')
        
        # 设置列宽
        tree.column('文件路径', width=300)
        tree.column('差异类型', width=100)
        tree.column('详细信息', width=300)
        
        # 添加数据
        for file_path, diff_type, detail in self.different_files:
            tree.insert('', tk.END, values=(str(file_path), diff_type, detail))
            
        # 添加滚动条
        scrollbar_v = ttk.Scrollbar(diff_window, orient=tk.VERTICAL, command=tree.yview)
        scrollbar_h = ttk.Scrollbar(diff_window, orient=tk.HORIZONTAL, command=tree.xview)
        tree.configure(yscrollcommand=scrollbar_v.set, xscrollcommand=scrollbar_h.set)
        
        # 布局
        tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(10, 0), pady=10)
        scrollbar_v.pack(side=tk.RIGHT, fill=tk.Y, pady=10)
        scrollbar_h.pack(side=tk.BOTTOM, fill=tk.X, padx=10)
        
        # 按钮框架
        button_frame = ttk.Frame(diff_window)
        button_frame.pack(fill=tk.X, padx=10, pady=5)
        
        # 关闭按钮
        close_button = ttk.Button(button_frame, text="关闭", command=diff_window.destroy)
        close_button.pack(side=tk.RIGHT)
            
    def update_status(self, message):
        timestamp = time.strftime("%H:%M:%S")
        self.status_text.insert(tk.END, f"[{timestamp}] {message}\n")
        self.status_text.see(tk.END)
        self.parent_frame.update_idletasks()
        
    def start_sync(self):
        source = self.source_folder.get().strip()
        target = self.target_folder.get().strip()
        
        if not source or not target:
            messagebox.showerror("错误", "请选择源文件夹和目标文件夹")
            return
            
        if not os.path.exists(source):
            messagebox.showerror("错误", "源文件夹不存在")
            return
            
        if not os.path.exists(target):
            try:
                os.makedirs(target)
                self.update_status(f"已创建目标文件夹: {target}")
            except Exception as e:
                messagebox.showerror("错误", f"无法创建目标文件夹: {str(e)}")
                return
                
        if source == target:
            messagebox.showerror("错误", "源文件夹和目标文件夹不能相同")
            return
            
        try:
            # 添加到历史记录
            self.add_to_history(source, target)
            
            # 获取同步模式
            is_bidirectional = self.sync_mode.get() == "双向同步"
            
            # 初始同步
            if is_bidirectional:
                self.update_status("开始双向初始同步...")
                self.bidirectional_initial_sync(source, target)
            else:
                self.update_status("开始单向初始同步...")
                self.initial_sync(source, target)
            
            # 启动文件监控
            if is_bidirectional:
                # 双向同步：创建两个监控器
                self.event_handler = FileSyncHandler(
                    source, target, self.update_status, self.should_exclude_file,
                    is_bidirectional=True, conflict_resolver=lambda: self.conflict_resolution.get(),
                    sync_direction="A_to_B", global_sync_lock=self.global_sync_lock
                )
                self.event_handler_target = FileSyncHandler(
                    target, source, self.update_status, self.should_exclude_file,
                    is_bidirectional=True, conflict_resolver=lambda: self.conflict_resolution.get(),
                    sync_direction="B_to_A", global_sync_lock=self.global_sync_lock
                )
                
                self.observer = Observer()
                self.observer_target = Observer()
                
                self.observer.schedule(self.event_handler, source, recursive=True)
                self.observer_target.schedule(self.event_handler_target, target, recursive=True)
                
                self.observer.start()
                self.observer_target.start()
                
                self.update_status(f"双向文件同步已启动")
                self.update_status(f"监控器A: {source} → {target}")
                self.update_status(f"监控器B: {target} → {source}")
                self.update_status(f"冲突解决策略: {self.conflict_resolution.get()}")
                self.update_status("正在监控两个文件夹的变化...")
            else:
                # 单向同步：只创建一个监控器
                self.event_handler = FileSyncHandler(
                    source, target, self.update_status, self.should_exclude_file,
                    is_bidirectional=False, sync_direction="A_to_B", global_sync_lock=self.global_sync_lock
                )
                self.observer = Observer()
                self.observer.schedule(self.event_handler, source, recursive=True)
                self.observer.start()
                
                self.update_status("单向文件同步已启动，正在监控源文件夹变化...")
            
            self.is_syncing = True
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)
            
        except Exception as e:
            messagebox.showerror("错误", f"启动同步失败: {str(e)}")
            
    def initial_sync(self, source, target):
        """执行初始同步（在后台线程中运行）"""
        def sync_thread():
            try:
                source_path = Path(source)
                target_path = Path(target)
                
                # 首先统计需要同步的文件数量
                self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set("统计文件数量..."))
                files_to_sync = []
                
                for item in source_path.rglob('*'):
                    if item.is_file():
                        relative_path = item.relative_to(source_path)
                        # 检查是否应该排除此文件
                        if not self.should_exclude_file(relative_path):
                            files_to_sync.append((item, relative_path))
                
                total_files = len(files_to_sync)
                if total_files == 0:
                    self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set("没有文件需要同步"))
                    return
                
                # 设置进度条最大值
                self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_bar.config(maximum=total_files))
                
                # 开始同步文件
                for i, (item, relative_path) in enumerate(files_to_sync):
                    if not self.is_syncing:  # 检查是否被停止
                        break
                        
                    try:
                        target_file = target_path / relative_path
                        
                        # 确保目标目录存在
                        target_file.parent.mkdir(parents=True, exist_ok=True)
                        
                        # 复制文件
                        shutil.copy2(item, target_file)
                        
                        # 更新进度
                        progress = i + 1
                        self.parent_frame.winfo_toplevel().after(0, lambda p=progress, t=total_files: self.progress_var.set(f"初始同步: {p}/{t}"))
                        self.parent_frame.winfo_toplevel().after(0, lambda p=progress: self.progress_bar.config(value=p))
                        self.parent_frame.winfo_toplevel().after(0, lambda rp=relative_path: self.update_status(f"初始同步: {rp}"))
                        
                        # 每处理10个文件后稍作停顿，避免UI无响应
                        if i % 10 == 0:
                            time.sleep(0.01)
                            
                    except Exception as e:
                        self.parent_frame.winfo_toplevel().after(0, lambda err=str(e), rp=relative_path: self.update_status(f"同步失败 {rp}: {err}"))
                
                # 完成初始同步
                if self.is_syncing:
                    self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set(f"初始同步完成 ({total_files} 个文件)"))
                    self.parent_frame.winfo_toplevel().after(0, lambda: self.update_status(f"初始同步完成，共处理 {total_files} 个文件"))
                    
            except Exception as e:
                self.parent_frame.winfo_toplevel().after(0, lambda err=str(e): self.update_status(f"初始同步错误: {err}"))
        
        # 在后台线程中执行同步
        sync_thread_obj = threading.Thread(target=sync_thread, daemon=True)
        sync_thread_obj.start()
        
    def bidirectional_initial_sync(self, source, target):
        """执行双向初始同步（在后台线程中运行）"""
        def sync_thread():
            try:
                source_path = Path(source)
                target_path = Path(target)
                
                # 统计需要同步的文件
                self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set("分析文件差异..."))
                self.parent_frame.winfo_toplevel().after(0, lambda: self.update_status("正在扫描源文件夹..."))
                
                # 获取两个文件夹的所有文件
                source_files = {}
                target_files = {}
                
                # 扫描源文件夹
                source_count = 0
                for item in source_path.rglob('*'):
                    if not self.is_syncing:  # 检查是否被停止
                        return
                    if item.is_file():
                        relative_path = item.relative_to(source_path)
                        if not self.should_exclude_file(relative_path):
                            source_files[relative_path] = item
                            source_count += 1
                            # 每扫描100个文件更新一次状态
                            if source_count % 100 == 0:
                                self.parent_frame.winfo_toplevel().after(0, lambda c=source_count: self.update_status(f"已扫描源文件夹 {c} 个文件..."))
                                time.sleep(0.01)  # 让UI有机会更新
                            
                self.parent_frame.winfo_toplevel().after(0, lambda: self.update_status("正在扫描目标文件夹..."))
                
                # 扫描目标文件夹
                target_count = 0
                for item in target_path.rglob('*'):
                    if not self.is_syncing:  # 检查是否被停止
                        return
                    if item.is_file():
                        relative_path = item.relative_to(target_path)
                        if not self.should_exclude_file(relative_path):
                            target_files[relative_path] = item
                            target_count += 1
                            # 每扫描100个文件更新一次状态
                            if target_count % 100 == 0:
                                self.parent_frame.winfo_toplevel().after(0, lambda c=target_count: self.update_status(f"已扫描目标文件夹 {c} 个文件..."))
                                time.sleep(0.01)  # 让UI有机会更新
                
                self.root.after(0, lambda: self.update_status(f"扫描完成：源文件夹 {len(source_files)} 个文件，目标文件夹 {len(target_files)} 个文件"))
                
                # 分析需要同步的文件
                sync_operations = []
                
                # 仅在源文件夹中的文件 -> 复制到目标
                only_in_source = set(source_files.keys()) - set(target_files.keys())
                for rel_path in only_in_source:
                    sync_operations.append(('copy_to_target', rel_path, source_files[rel_path]))
                
                # 仅在目标文件夹中的文件 -> 复制到源
                only_in_target = set(target_files.keys()) - set(source_files.keys())
                for rel_path in only_in_target:
                    sync_operations.append(('copy_to_source', rel_path, target_files[rel_path]))
                
                # 两个文件夹都有的文件 -> 根据冲突解决策略处理
                common_files = set(source_files.keys()) & set(target_files.keys())
                for rel_path in common_files:
                    source_file = source_files[rel_path]
                    target_file = target_files[rel_path]
                    
                    # 检查文件是否不同
                    if self._files_are_different(source_file, target_file):
                        # 根据冲突解决策略决定同步方向
                        if self._should_sync_source_to_target(source_file, target_file):
                            sync_operations.append(('copy_to_target', rel_path, source_file))
                        else:
                            sync_operations.append(('copy_to_source', rel_path, target_file))
                
                total_operations = len(sync_operations)
                if total_operations == 0:
                    self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set("文件夹已同步"))
                    self.parent_frame.winfo_toplevel().after(0, lambda: self.update_status("双向初始同步完成：文件夹已保持同步"))
                    return
                
                # 设置进度条
                self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_bar.config(maximum=total_operations))
                
                # 执行同步操作
                for i, (operation, rel_path, file_path) in enumerate(sync_operations):
                    if not self.is_syncing:
                        break
                        
                    try:
                        if operation == 'copy_to_target':
                            target_file = target_path / rel_path
                            target_file.parent.mkdir(parents=True, exist_ok=True)
                            shutil.copy2(file_path, target_file)
                            self.parent_frame.winfo_toplevel().after(0, lambda rp=rel_path: self.update_status(f"双向同步 →: {rp}"))
                        elif operation == 'copy_to_source':
                            source_file = source_path / rel_path
                            source_file.parent.mkdir(parents=True, exist_ok=True)
                            shutil.copy2(file_path, source_file)
                            self.parent_frame.winfo_toplevel().after(0, lambda rp=rel_path: self.update_status(f"双向同步 ←: {rp}"))
                        
                        # 更新进度
                        progress = i + 1
                        self.parent_frame.winfo_toplevel().after(0, lambda p=progress, t=total_operations: self.progress_var.set(f"双向同步: {p}/{t}"))
                        self.parent_frame.winfo_toplevel().after(0, lambda p=progress: self.progress_bar.config(value=p))
                        
                        # 每处理10个文件后稍作停顿
                        if i % 10 == 0:
                            time.sleep(0.01)
                            
                    except Exception as e:
                        self.parent_frame.winfo_toplevel().after(0, lambda err=str(e), rp=rel_path: self.update_status(f"同步失败 {rp}: {err}"))
                
                # 完成双向初始同步
                if self.is_syncing:
                    self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set(f"双向初始同步完成 ({total_operations} 个操作)"))
                    self.parent_frame.winfo_toplevel().after(0, lambda: self.update_status(f"双向初始同步完成，共执行 {total_operations} 个同步操作"))
                    
            except Exception as e:
                self.parent_frame.winfo_toplevel().after(0, lambda err=str(e): self.update_status(f"双向初始同步错误: {err}"))
        
        # 在后台线程中执行双向同步
        sync_thread_obj = threading.Thread(target=sync_thread, daemon=True)
        sync_thread_obj.start()
        
    def _files_are_different(self, file1, file2):
        """检查两个文件是否不同"""
        try:
            stat1 = file1.stat()
            stat2 = file2.stat()
            
            # 先比较大小
            if stat1.st_size != stat2.st_size:
                return True
                
            # 再比较修改时间（允许1秒的误差）
            if abs(stat1.st_mtime - stat2.st_mtime) > 1:
                return True
                
            # 最后比较哈希值（仅对小文件）
            if stat1.st_size < 1024 * 1024:  # 小于1MB的文件
                hash1 = self.get_file_hash(file1)
                hash2 = self.get_file_hash(file2)
                return hash1 != hash2
                
            return False
        except Exception:
            return True  # 出错时认为文件不同
            
    def _should_sync_source_to_target(self, source_file, target_file):
        """根据冲突解决策略决定是否应该从源同步到目标"""
        try:
            strategy = self.conflict_resolution.get()
            source_stat = source_file.stat()
            target_stat = target_file.stat()
            
            if strategy == "较新文件优先":
                return source_stat.st_mtime > target_stat.st_mtime
            elif strategy == "较大文件优先":
                return source_stat.st_size > target_stat.st_size
            elif strategy == "A文件夹优先":
                return True  # 总是优先A文件夹
            else:
                return source_stat.st_mtime > target_stat.st_mtime  # 默认较新优先
                
        except Exception:
            return True  # 出错时默认同步
                
    def stop_sync(self):
        # 停止源文件夹监控器
        if self.observer and self.observer.is_alive():
            self.observer.stop()
            self.observer.join()
            
        # 停止目标文件夹监控器（双向同步时）
        if hasattr(self, 'observer_target') and self.observer_target and self.observer_target.is_alive():
            self.observer_target.stop()
            self.observer_target.join()
            
        self.is_syncing = False
        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
        
        # 重置进度条
        self.progress_bar.config(value=0)
        self.progress_var.set("同步已停止")
        
        # 根据同步模式显示不同的停止消息
        if self.sync_mode.get() == "双向同步":
            self.update_status("双向文件同步已停止")
        else:
            self.update_status("单向文件同步已停止")
        
    def get_file_hash(self, file_path):
        """计算文件的MD5哈希值"""
        hash_md5 = hashlib.md5()
        try:
            with open(file_path, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    hash_md5.update(chunk)
            return hash_md5.hexdigest()
        except Exception:
            return None
            
    def compare_folders(self):
        """对比两个文件夹的内容"""
        source = self.source_folder.get().strip()
        target = self.target_folder.get().strip()
        
        if not source or not target:
            messagebox.showerror("错误", "请先选择源文件夹和目标文件夹")
            return
            
        if not os.path.exists(source):
            messagebox.showerror("错误", "源文件夹不存在")
            return
            
        if not os.path.exists(target):
            messagebox.showerror("错误", "目标文件夹不存在")
            return
            
        # 添加到历史记录
        self.add_to_history(source, target)
        
        # 重置进度和差异列表
        self.different_files = []
        self.progress_var.set("准备对比...")
        self.progress_bar['value'] = 0
        
        # 根据同步模式显示不同的对比状态
        if self.sync_mode.get() == "双向同步":
            self.update_status("开始对比文件夹（双向模式）...")
        else:
            self.update_status("开始对比文件夹（单向模式）...")
        
        # 在新线程中执行对比，避免界面卡顿
        threading.Thread(target=self._compare_folders_thread, args=(source, target), daemon=True).start()
        
    def _compare_folders_thread(self, source, target):
        """在后台线程中执行文件夹对比"""
        try:
            source_path = Path(source)
            target_path = Path(target)
            
            # 获取所有文件
            source_files = {}
            target_files = {}
            
            # 更新进度
            self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set("扫描源文件夹..."))
            
            # 扫描源文件夹
            for item in source_path.rglob('*'):
                if item.is_file():
                    relative_path = item.relative_to(source_path)
                    # 检查是否应该排除此文件
                    if not self.should_exclude_file(relative_path):
                        source_files[relative_path] = item
                    
            # 更新进度
            self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set("扫描目标文件夹..."))
            
            # 扫描目标文件夹
            for item in target_path.rglob('*'):
                if item.is_file():
                    relative_path = item.relative_to(target_path)
                    # 检查是否应该排除此文件
                    if not self.should_exclude_file(relative_path):
                        target_files[relative_path] = item
                    
            # 计算总文件数
            total_files = len(source_files) + len(set(target_files.keys()) - set(source_files.keys()))
            processed_files = 0
            
            # 更新进度
            self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set(f"对比文件 (0/{total_files})"))
            self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_bar.config(maximum=total_files))
            
            # 找出差异
            only_in_source = set(source_files.keys()) - set(target_files.keys())
            only_in_target = set(target_files.keys()) - set(source_files.keys())
            common_files = set(source_files.keys()) & set(target_files.keys())
            
            # 处理仅在源文件夹中的文件
            for file_path in only_in_source:
                processed_files += 1
                # 每处理20个文件更新一次进度，减少UI更新频率
                if processed_files % 20 == 0 or processed_files == total_files:
                    progress_text = f"对比文件 ({processed_files}/{total_files})"
                    self.parent_frame.winfo_toplevel().after(0, lambda text=progress_text: self.progress_var.set(text))
                    self.parent_frame.winfo_toplevel().after(0, lambda value=processed_files: self.progress_bar.config(value=value))
                
                diff_info = (file_path, "仅在源文件夹", "目标文件夹中不存在此文件")
                self.different_files.append(diff_info)
                    
            # 处理仅在目标文件夹中的文件
            for file_path in only_in_target:
                processed_files += 1
                # 每处理20个文件更新一次进度
                if processed_files % 20 == 0 or processed_files == total_files:
                    progress_text = f"对比文件 ({processed_files}/{total_files})"
                    self.parent_frame.winfo_toplevel().after(0, lambda text=progress_text: self.progress_var.set(text))
                    self.parent_frame.winfo_toplevel().after(0, lambda value=processed_files: self.progress_bar.config(value=value))
                
                diff_info = (file_path, "仅在目标文件夹", "源文件夹中不存在此文件")
                self.different_files.append(diff_info)
                    
            # 对比共同文件的内容
            for i, file_path in enumerate(common_files):
                processed_files += 1
                # 每处理20个文件更新一次进度
                if processed_files % 20 == 0 or processed_files == total_files:
                    progress_text = f"对比文件 ({processed_files}/{total_files})"
                    self.parent_frame.winfo_toplevel().after(0, lambda text=progress_text: self.progress_var.set(text))
                    self.parent_frame.winfo_toplevel().after(0, lambda value=processed_files: self.progress_bar.config(value=value))
                
                source_file = source_files[file_path]
                target_file = target_files[file_path]
                
                try:
                    # 对比文件大小
                    source_size = source_file.stat().st_size
                    target_size = target_file.stat().st_size
                    
                    if source_size != target_size:
                        diff_info = (file_path, "大小不同", f"A:{source_size}字节 vs B:{target_size}字节")
                        self.different_files.append(diff_info)
                        continue
                        
                    # 对比文件内容（通过哈希值）
                    source_hash = self.get_file_hash(source_file)
                    target_hash = self.get_file_hash(target_file)
                    
                    if source_hash and target_hash and source_hash != target_hash:
                        diff_info = (file_path, "内容不同", "文件内容哈希值不匹配")
                        self.different_files.append(diff_info)
                        
                except Exception as e:
                    diff_info = (file_path, "对比错误", f"无法对比文件: {str(e)}")
                    self.different_files.append(diff_info)
                
                # 每处理50个文件后稍作停顿，避免CPU占用过高
                if i % 50 == 0:
                    time.sleep(0.01)
                    
            # 完成进度
            self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_var.set("对比完成"))
            self.parent_frame.winfo_toplevel().after(0, lambda: self.progress_bar.config(value=total_files))
            
            # 启用差异列表按钮
            if self.different_files:
                self.parent_frame.winfo_toplevel().after(0, lambda: self.diff_list_button.config(state=tk.NORMAL))
                    
            # 总结报告
            total_differences = len(self.different_files)
            if total_differences == 0:
                self.update_status("✓ 对比完成：两个文件夹内容完全一致！")
            else:
                self.update_status(f"✗ 对比完成：发现 {total_differences} 个差异")
                self.update_status("点击'查看差异列表'按钮查看详细信息")
                self.update_status("建议：点击'开始同步'来同步文件夹内容")
                
        except Exception as e:
            self.update_status(f"对比过程中出现错误: {str(e)}")
            
    def on_closing(self):
        """程序关闭时的处理"""
        # 保存历史记录和排除规则
        self.save_history()
        self.save_exclude_rules()
        
        if self.is_syncing:
            if messagebox.askokcancel("退出", "同步正在进行中，确定要退出吗？"):
                self.stop_sync()
                self.parent_frame.winfo_toplevel().destroy()
        else:
            self.parent_frame.winfo_toplevel().destroy()

def main():
    root = tk.Tk()
    app = MainApp(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()

if __name__ == "__main__":
    main()